package com.hotel.writer.lucene.index;

import org.apache.lucene.document.Document;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.Term;
import org.apache.lucene.index.TrackingIndexWriter;
import org.apache.lucene.search.*;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;
import org.wltea.analyzer.lucene.IKAnalyzer;

import java.io.File;
import java.io.IOException;

/**
 * lucene近实时搜索<br/>
 * <p/>
 * 在此项目中用不到，此项目需要有一个indexWriter负责写文件，同时提供多个indexSearch支持搜索<br/>
 * 所以需要部署一个writer项目来写索引文件(多个writer写同一个文件会有锁的问题)<br/>
 * 部署多个search项目来支持并发的读索引文件(用户量比较大的时候，仅部署一个search项目会有很大压力)
 *
 * @author XuSemon
 * @date 2015-4-13
 */
public class LuceneIndexManager {
    private static final Version LUCENE_VERSION = Version.LUCENE_44;
    /**
     * 索引文件Writer类
     */
    private IndexWriter writer;
    /**
     * 索引文件目录类
     */
    private Directory directory = null;
    /**
     *
     */
    private TrackingIndexWriter trackingIndexWriter = null;
    /**
     * 近实时搜索管理類
     */
    private ReferenceManager<IndexSearcher> reMgr = null;// 类似于Lucene3.x中的NrtManager
    /**
     *
     */
    private ControlledRealTimeReopenThread<IndexSearcher> crt = null;
    /**
     * lucene近实时搜索静态类
     */
    private static LuceneIndexManager instance;

    // 数据初始化
    private LuceneIndexManager(String fsPath) {
        init(fsPath);
    }

    @SuppressWarnings({"unchecked", "rawtypes"})
    private void init(String fsPath) {
        try {
            directory = FSDirectory.open(new File(fsPath));
            //创建writer  
            writer = new IndexWriter(directory, new IndexWriterConfig(LUCENE_VERSION, new IKAnalyzer()));

            trackingIndexWriter = new TrackingIndexWriter(writer);
            //新建SearcherManager  
            //true 表示在内存中删除，false可能删可能不删，设为false性能会更好一些
            reMgr = new SearcherManager(writer, true, new SearcherFactory());
            //ControlledRealTimeReopenThread 构造是必须有的，主要将writer装，每个方法都没有commit 操作。
            //在0.025s~5.0s之间重启一次线程，这个是时间的最佳实践
            crt = new ControlledRealTimeReopenThread(trackingIndexWriter, reMgr, 5.0, 0.025);
            crt.setDaemon(true);//设置为后台服务
            crt.setName("NrtManager Reopen Thread");//线程名称
            crt.start();//线程启动
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 使用单例获取IndexSearch *
     */
    public IndexSearcher getSearcher() {
        IndexSearcher is = null;
        try {
            if (is == null) {
                reMgr.maybeRefresh();//刷新reMgr,获取最新的IndexSearcher
                is = reMgr.acquire();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        if (is == null) {
            throw new RuntimeException("indexSearcher is null!!!!");
        }
        return is;
    }

    /**
     * 建立索引
     * 要实现search nrt,需要使用TrackIndexWriter保存document，同时Writer也不需要关闭。
     * <p/>
     * *
     */
    public void index(Document doc) {
        try {
            trackingIndexWriter.addDocument(doc);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 修改
     * 使用trackIndexWriter进行修改，不需要关闭writer
     * *
     */
    public void update(Term term, Document doc) {
        try {
            trackingIndexWriter.updateDocument(term, doc);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 删除
     * 使用trackIndexWriter进行数据删除，也不需要关闭Writer
     * *
     */
    public void delete(Term term) {
        try {
            trackingIndexWriter.deleteDocuments(term);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 定期提交内存中得索引到硬盘上，防止丢失
     */
    public void commit() {
        try {
            writer.commit();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void close() {
        //stop the re-open thread
        crt.interrupt();
        crt.close();
        //close the indexWriter,commit 所有有过修改的内容
        try {
            writer.commit();
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static synchronized LuceneIndexManager getInstance(String fsPath) {
        if (instance == null) {
            instance = new LuceneIndexManager(fsPath);
        }
        return instance;
    }
}
